/* Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "transfer/mctp_transfer.h"

// =================================================== send/receive ===================================================
uint32_t MctpTransfer::DeviceSendMessage(void *spdm_context, size_t message_size, const void *message,
    uint64_t timeout)
{
    endRecvTempBufferLen = sizeof(sendRecvTempBuffer);
    int32_t res = LCallbacks::send_recv(this->device_name, reinterpret_cast<const uint8_t *>(message), message_size,
        sendRecvTempBuffer, &endRecvTempBufferLen);
    return res == RET_OK ? LIBSPDM_STATUS_SUCCESS : LIBSPDM_STATUS_SESSION_MSG_ERROR;
}

uint32_t MctpTransfer::DeviceReceiveMessage(void *spdm_context, size_t *message_size, void **message,
    uint64_t timeout)
{
    if (*message == nullptr) {
        debug_log(DLOG_ERROR, "invalid parameter");
        return LIBSPDM_STATUS_SESSION_MSG_ERROR;
    }

    memcpy_s(reinterpret_cast<uint8_t *>(*message), *message_size, sendRecvTempBuffer, endRecvTempBufferLen);

    *message_size = endRecvTempBufferLen;
    memset_s(sendRecvTempBuffer, SENDER_RECEIVER_MAX_BUFFER_SIZE, 0, SENDER_RECEIVER_MAX_BUFFER_SIZE);
    endRecvTempBufferLen = SENDER_RECEIVER_MAX_BUFFER_SIZE;

    return LIBSPDM_STATUS_SUCCESS;
}

uint32_t MctpSendCallback(void *spdm_context, size_t message_size, const void *message,
    uint64_t timeout)
{
    libspdm_context_t *ctx = static_cast<libspdm_context_t *>(spdm_context);
    MctpTransfer *transfer = static_cast<MctpTransfer *>(ctx->app_context_data_ptr);

    return transfer->DeviceSendMessage(spdm_context, message_size, message, timeout);
}

uint32_t MctpReceiveCallback(void *spdm_context, size_t *message_size, void **message,
    uint64_t timeout)
{
    libspdm_context_t *ctx = static_cast<libspdm_context_t *>(spdm_context);
    MctpTransfer *transfer = static_cast<MctpTransfer *>(ctx->app_context_data_ptr);

    return transfer->DeviceReceiveMessage(spdm_context, message_size, message, timeout);
}

// =================================================== encode/decode ===================================================
uint32_t MctpTransfer::EncodeMessage(void *spdm_context, const uint32_t *session_id, bool is_app_message,
    bool is_request_message, size_t message_size, void *message, size_t *transport_message_size,
    void **transport_message)
{
    // TODO 数据编码动作
    return LIBSPDM_STATUS_SUCCESS;
}

uint32_t MctpTransfer::DecodeMessage(void *spdm_context, uint32_t **session_id, bool *is_app_message,
    bool is_request_message, size_t transport_message_size, void *transport_message, size_t *message_size,
    void **message)
{
    // TODO 数据解码动作
    return LIBSPDM_STATUS_SUCCESS;
}

uint32_t MctpEncodeCallback(void *spdm_context, const uint32_t *session_id, bool is_app_message,
    bool is_request_message, size_t message_size, void *message, size_t *transport_message_size,
    void **transport_message)
{
    libspdm_context_t *ctx = static_cast<libspdm_context_t *>(spdm_context);
    MctpTransfer *transfer = static_cast<MctpTransfer *>(ctx->app_context_data_ptr);

    return transfer->EncodeMessage(spdm_context, session_id, is_app_message, is_request_message, message_size, message,
        transport_message_size, transport_message);
}

uint32_t MctpDecodeCallback(void *spdm_context, uint32_t **session_id, bool *is_app_message,
    bool is_request_message, size_t transport_message_size,void *transport_message, size_t *message_size,
    void **message)
{
    libspdm_context_t *ctx = static_cast<libspdm_context_t *>(spdm_context);
    MctpTransfer *transfer = static_cast<MctpTransfer *>(ctx->app_context_data_ptr);

    return transfer->DecodeMessage(spdm_context, session_id, is_app_message, is_request_message,
        transport_message_size, transport_message, message_size, message);
}

// =================================================== buffer management ===================================================
uint32_t MctpTransfer::AcquireSenderBuffer(void *spdm_context, void **msg_buf_ptr)
{
    if (sendReceiveBufferAcquired) {
        return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
    }

    *msg_buf_ptr = sendReceiveBuffer;
    ZeroMem(sendReceiveBuffer, sizeof(sendReceiveBuffer));
    sendReceiveBufferAcquired = true;

    return LIBSPDM_STATUS_SUCCESS;
}

void MctpTransfer::ReleaseSenderBuffer(void *spdm_context, const void *msg_buf_ptr)
{
    sendReceiveBufferAcquired = false;
}

uint32_t MctpTransfer::AcquireReceiverBuffer(void *spdm_context, void **msg_buf_ptr)
{
    if (sendReceiveBufferAcquired) {
        return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
    }

    *msg_buf_ptr = sendReceiveBuffer;
    ZeroMem(sendReceiveBuffer, sizeof(sendReceiveBuffer));
    sendReceiveBufferAcquired = true;

    return LIBSPDM_STATUS_SUCCESS;
}

void MctpTransfer::ReleaseReceiverBuffer(void *spdm_context, const void *msg_buf_ptr)
{
    sendReceiveBufferAcquired = false;
}

uint32_t MctpAcquireSenderBufferCallback(void *spdm_context, void **msg_buf_ptr)
{
    libspdm_context_t *ctx = static_cast<libspdm_context_t *>(spdm_context);
    MctpTransfer *transfer = static_cast<MctpTransfer *>(ctx->app_context_data_ptr);

    return transfer->AcquireSenderBuffer(spdm_context, msg_buf_ptr);
}

void MctpReleaseSenderBufferCallback(void *spdm_context, const void *msg_buf_ptr)
{
    libspdm_context_t *ctx = static_cast<libspdm_context_t *>(spdm_context);
    MctpTransfer *transfer = static_cast<MctpTransfer *>(ctx->app_context_data_ptr);

    return transfer->ReleaseSenderBuffer(spdm_context, msg_buf_ptr);
}

uint32_t MctpAcquireReceiverBufferCallback(void *spdm_context, void **msg_buf_ptr)
{
    libspdm_context_t *ctx = static_cast<libspdm_context_t *>(spdm_context);
    MctpTransfer *transfer = static_cast<MctpTransfer *>(ctx->app_context_data_ptr);

    return transfer->AcquireReceiverBuffer(spdm_context, msg_buf_ptr);
}

void MctpReleaseReceiverBufferCallback(void *spdm_context, const void *msg_buf_ptr)
{
    libspdm_context_t *ctx = static_cast<libspdm_context_t *>(spdm_context);
    MctpTransfer *transfer = static_cast<MctpTransfer *>(ctx->app_context_data_ptr);

    return transfer->ReleaseReceiverBuffer(spdm_context, msg_buf_ptr);
}

// =================================================== register ===================================================
int32_t MctpTransfer::RegisterFunctions(void *spdm_context)
{
    libspdm_return_t status = SpdmSetData(spdm_context, LIBSPDM_DATA_APP_CONTEXT_DATA, nullptr, this, sizeof(void *));
    if (status != LIBSPDM_STATUS_SUCCESS) {
        debug_log(DLOG_ERROR, "set transfer context data failed");
        return RET_ERR;
    }

    RegisterDeviceIoFunc(spdm_context, MctpSendCallback, MctpReceiveCallback);
    RegisterTransportLayerFunc(spdm_context, msgMaxSize, msgHeaderSize, msgTailSize, MctpEncodeCallback, MctpDecodeCallback);
    RegisterDeviceBufferFunc(spdm_context, SENDER_BUFFER_SIZE, RECEIVER_BUFFER_SIZE,
        MctpAcquireSenderBufferCallback, MctpReleaseSenderBufferCallback,
        MctpAcquireReceiverBufferCallback, MctpReleaseReceiverBufferCallback);

    size_t scratch_buffer_size = GetRequiredScratchBufferSize(spdm_context);
    debug_log(DLOG_INFO, "scratch_buffer_size = %lu", scratch_buffer_size);
    if (scratch_buffer_size != 0) {
        scratchBuffer = (void *)malloc(scratch_buffer_size);
        if (scratchBuffer == nullptr) {
            debug_log(DLOG_ERROR, "malloc scratch buffer failed");
            return RET_ERR;
        }
        SetScratchBuffer(spdm_context, scratchBuffer, scratch_buffer_size);
    }
    
    return RET_OK;
}